iT邦幫忙

1

HttpClient 使用注意事項

  • 分享至 

  • xImage
  •  

HttpClient 使用注意事項

小明回報系統錯誤紀錄噴出以下例外錯誤

Unable to connect to the remote server
InnerException
Only one usage of each socket address (protocol/network address/port) is normally permitted

重新檢視小明的原始碼

public string GetContent()
{
    var httpClient = new HttpClient();
    httpClient.BaseAddress = new Uri("http://blog.flash.com/");
    httpClient.DefaultRequestHeaders.ConnectionClose = true;
    var result = httpclient.GetAsync("").GetAwaiter().GetResult();
    return result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
}

發現HttpClient 有實作IDisposable 介面, 依據.NET 開發人員多年所受訓練的反射動作: 遇到IDiposable 就該用using 包覆使用範圍, 用畢要儘早釋放.

public string GetContent()
{
    using(var httpClient = new HttpClient())
    {
        httpClient.BaseAddress = new Uri("http://blog.flash.com/");
        httpClient.DefaultRequestHeaders.ConnectionClose = true;
        var result = httpclient.GetAsync("").GetAwaiter().GetResult();
        return result.Content.ReadAsStringAsync().GetAwaiter().GetResult();
    }
}

結果仍然還是有同樣的問題, 查了原因是由於HttpClient 底層沒有實作Connetion Pool 無法重複使用連線, 程式將對同一個台網站開啟 100 條Socket 連線, 而這100 條連線在HttpClient Dispose() 後會以TIME_WAIT 狀態繼續存活240 秒才被真的釋放. 當殘留連線數量龐大時, 用光可用Socket Port 號碼, 導致無法再建立新連線.

對於HttpClient 的最佳使用方式, 一派主張因每個HttpClient 都會建立新連線, 為免耗盡Socket 資源應儘量共用. 另一派則認為長期使用現在連線, 會遇到DNS 異動將無法更新. 故設計人員將HttpClient 何時建立及消滅設成選項, 讓開發者自己選.

發現ASP.NET Core 2.1 / .NET 4.6 起推出HttpClientFactory 一舉改善前述問題.

For .NET Core

.NET Core 已預設參考Microsoft.AspNetCore.App ,已內建Microsoft.Extensions.Http plugin ,故無須額外安裝套件

Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddHttpClient();
}

在需要使用 HttpClient 的 class 中透過 .NET Core 的建構式注入

private readonly IHttpClientFactory _clientFactory;
public HomeController(IHttpClientFactory clientFactory)
{
    _clientFactory = clientFactory;
}

需要使用HttpClient 直接透過CreateClient 方法取得即可

using(var client = _clientFactory.CreateClient())
{
    ...
}

或者你也能在註冊時候指定名稱及預做基本設定

services.AddHttpClient("flashblog", c =>
{
    c.BaseAddress = new Uri("https://blog.flash.com/");
});

在需要的時候直接透過CreateClient 方法並指定名稱即可

var client = _clientFactory.CreateClient("flashblog");

For .NET 4.6+

var client = HttpClientFactory.CreateClient();
...

For .NET 3.5/4.0

WebClient 方法沒被設計成Thread-Safe (多緒執行時會丟 NotSupportedException), 使用時用完即拋策略就對了, 每次建立WebClient 都會開新連線, 但它已是昨日黃花, 寫新程式就別再用WebClient , 改用 HttpClient 吧!(除非是 .NET 3.5/4.0)

題外話

Windows 可使用的port 設定可以查詢

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\MaxUserPort

Windows 10 預設值為15000

Windows 環境可以透過下面設定縮短TIME-WAIT 時間

HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Tcpip\Parameters\TcpTimedWaitDelay

圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言